    #pragma once

#include <fstream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include "main.h"

using namespace std;

const ui32 MAX_READ_BUF_SIZE = ui32(512)<<3;
const ui32 MAX_WRITE_BUF_SIZE = ui32(512)<<6;

/*
 * UnConst is used for clearing 'const' from a given type.
 */
template <class U> struct UnConst {
	typedef U Result;
};

template <class U> struct UnConst<const U> {
	typedef U Result;
};


/*
 * Interfaces
 */

class IDataReader {
public:
    virtual ui64 ReadData(char *output, ui64 amount) = 0;
    virtual void Rewind() = 0;
//    virtual ui64 ReadInto(ui64) = 0;
    virtual ui64 Size() = 0;
};
class IBuferedReader {
    virtual ui64 ReadInto(ui64) = 0;
};

class IDataWriter {
public:
    virtual bool WriteData(const char* input, ui64 amount) = 0;
};

template <int direction, typename TStorage>
class TTDataWriter;

class IBitReader {
public:
//    virtual void Read(ui32& result, const ui32 bits) = 0;
//    virtual void Read64(ui64& result, const ui32 bits) = 0;
    virtual void ReadABit(ui64& result) = 0;
};

class IBitWriter {
public:
    virtual ui64 GetOffset() const = 0;
    //virtual void Write(ui64 data, ui32 bits) = 0;
    virtual void Flush() = 0;
};

/*
 * Byte I/O
 */
template <typename TSource>
class TTDataReader;

template <class TDataReader>
class TTBufferdDataReader;

//template <>
//class TTDataReader<ifstream*> : public IDataReader {
//};

template <>
class TTDataReader<ifstream*> : public IDataReader {
    typedef ifstream* TSource;
    ifstream* In;
public:
    TTDataReader(ifstream* in) : In(in) {}
    ui64 ReadData(char *to, ui64 count) {
        return In->readsome(to,count);
    }
    template <typename TInto>
    ui64 ReadInto(TInto &result) {
        return In->readsome((char*)&result,sizeof(TInto));
    }
    ui64 Size() {
        if (!In->is_open() || In->rdstate() != ios_base::goodbit) {
            return 0;
        } else {
            ifstream::pos_type current_position = In->tellg();
            In->seekg(0,ifstream::end);
            ifstream::pos_type end_position = In->tellg();
            In->seekg((int)current_position,ifstream::beg);
            return (end_position - current_position);
        }
    }
};



template <>
class TTDataReader<FILE*>  : public IDataReader {
	enum {
		BUF_SIZE = MAX_READ_BUF_SIZE
	};
	FILE* In;
	char* ActiveBufferPointer;
	ui64 RealBufferSize;
public:
    typedef FILE* TSource;
    char BufferPointer[BUF_SIZE];

	TTDataReader(FILE* in) : In(in),  RealBufferSize(0) {
		LoadBuffer();
    }
	ui64 ReadData(char *output, ui64 amount) {

		ui64 readed = 0;

        // while we need some thing to read and have something read
        while (IsNotEmpty() && amount >0) {
            // remember current buffer size
            const ui64 active_size =  GetActiveSize();
            // choose how many bytes to read
            const bool is_there_enough = amount < active_size;
            const ui64 bytes_to_copy = is_there_enough  ? amount : active_size;
            // copy data to output
            memcpy(output, ActiveBufferPointer, bytes_to_copy);
            if (is_there_enough) {
                // change buffer active size
                ActiveBufferPointer += amount;
            } else {
				// or load it with new data if it goes out
                LoadBuffer();
                // shift output address
                output += bytes_to_copy;
            }
            // decrease requested amount
            amount -= bytes_to_copy;
            // count readed bytes
            readed += bytes_to_copy;
        }

		return readed;
	}
	template <typename TInto>
	ui64 ReadInto(TInto &result) {
		SASSERT((sizeof(TInto) <= 8))

		ui64 readed = 0;
		const ui64 active_size =  GetActiveSize();

		if (active_size >= sizeof(TInto)) {
			result = *(const TInto*)ActiveBufferPointer;
            ActiveBufferPointer += sizeof(TInto);
			readed = sizeof(TInto);
        } else {
			readed = ReadData((char*)&result, sizeof(TInto));
        }

		return readed;
    }
    ~TTDataReader() {
	}
	void Rewind() {
		rewind(In);
		LoadBuffer();
	}
	// need testing
    ui64 Size() {

		ui64 res = 0;

		if (!ferror(In)) {
            const ui64 current_position = ftell(In);
            fseek(In, 0, SEEK_END);
            const ui64 end_position = ftell(In);
            fseek(In, current_position, SEEK_SET);		
			res = RealBufferSize + end_position - current_position;
        }

		return res;
    }
private:
    ui64 GetActiveSize() {
//		DEBUG_ASSERT(BufferPointer + RealBufferSize - ActiveBufferPointer >= 0);
		return BufferPointer + RealBufferSize - ActiveBufferPointer;
    }
    void LoadBuffer() {
        RealBufferSize = fread(BufferPointer,1,BUF_SIZE,In);
        ActiveBufferPointer = BufferPointer;
	}
    bool IsNotEmpty() {
		bool res = false;
        if (ActiveBufferPointer != BufferPointer)
			res = true;
        else
			res = RealBufferSize;
		return res;
    }
};


template<int direction>
class TTDataWriter<direction, vector<char>*> : public IDataWriter {
    typedef vector<char>* TStorage;
    vector<char>* Data;
public:
    bool WriteData(const char* input, ui64 amount) {
        if (direction) {
            Data->insert(Data->end(), input, input+amount);
        } else {
            Data->insert(Data->begin(), input, input+amount);
        }
        return 0;
    }
    TTDataWriter(vector<char>* data) : Data(data) {}
};

template<>
class TTDataWriter<1, ofstream*> : public IDataWriter {
    typedef ofstream* TStorage;
    ofstream* Out;
public:
    bool WriteData(const char* input, ui64 amount) {
        Out->write(input, amount);
        return Out->rdstate();
    }
    TTDataWriter(ofstream* out) : Out(out) {}
    ~TTDataWriter() {
        Out->flush();
    }
};

template<>
class TTDataWriter<1, FILE*> : public IDataWriter
{
    enum {
        BUF_SIZE = MAX_WRITE_BUF_SIZE
    };
    char BufferPointer[BUF_SIZE];
    char* FreeBufferPointer;
    FILE* Out;
public:    
    typedef FILE* TStorage;
    //NEED TESTING
    virtual ~TTDataWriter() {
        FlushData();
    }
    bool WriteData(const char* input, ui64 amount) {
        // if amount of data to write lesser than amount of free space
        if (amount < GetFreeSpace()) {
            // copy all data from input into the buffer
            // if amount not less then size of qword
            if (amount <= sizeof(ui64)) {
                // use 64bit register to copy
				CopyToBuffer(*(const ui64*)input);
            }
            // if amount less then size of qword
            else {
                // use memcopy
				CopyToBuffer(input,amount);
			}
        // if amount of data to write more or equal to amount of free space
        } else {
            // flush
            FlushData();
            // if amount of data to write more then quarter of buffer size
            if (amount > BUF_SIZE>>4) {
				// write all it staright to output
                fwrite(input,1,amount,Out);
            }
            else {
				// copy all data from input into the buffer
				CopyToBuffer(input,amount);
			}
        }
        return (ferror(Out));
    }
    template <typename TData>
    void WriteWhole(TData data) {
        // check if input data have size not above qword
        SASSERT((sizeof(TData) <= 8))
		// check if buffer is wide enough
        SASSERT((sizeof(TData) <= BUF_SIZE))
        // set amount of bytes to write to size of input data
		const ui32 to_write = sizeof(TData);
        const ui64 free_space = GetFreeSpace();
        // if amount of data to write lesser than amount of free space
		if (to_write <= free_space) {
			// copy data into the buffer
			CopyToBuffer(data);
		}
        // if amount of data to write equal to amount of free space
		else if (to_write == free_space) {
			// copy data into the buffer
			CopyToBuffer(data);
			// flush
            FlushData();
        }
        // if amount of data to write more then amount of free space
        else {
            // flush
            FlushData();
			// copy data into the buffer
			CopyToBuffer(data);
		}
    }
    TTDataWriter(FILE* out) : Out(out) {
        FreeBufferPointer = BufferPointer;
    }
private:
	void CopyToBuffer(const char *input, ui32 amount) {
		// copy all data from input into the buffer
		memcpy(FreeBufferPointer,input,amount);
		// shift free space pointer
		FreeBufferPointer += amount;
	}
	template <typename TData>
	void CopyToBuffer(TData &data) {
		// get rid of 'const'
		typedef typename UnConst<TData>::Result TVarData;
		// copy data into the buffer
		*(TVarData*)FreeBufferPointer = data;
		// shift free space pointer
		FreeBufferPointer += sizeof(TData);
	}
	ui64 GetFreeSpace() {
        return (BufferPointer - FreeBufferPointer) + BUF_SIZE;
    }
    void FlushData() {
        fwrite(BufferPointer,1,FreeBufferPointer-BufferPointer,Out);
        FreeBufferPointer = BufferPointer;
    }
    bool IsNotFull() {
        return (FreeBufferPointer != BufferPointer+BUF_SIZE);
    }
};

/*
 * Bit I/O
 */

template <class TDataReader>
class TTBitReader : public TDataReader, public IBitReader
{    
    //TDataReader *DataReader;
    ui64 Remains;
    ui32 RemainsNum;
public:
    //    TTBitReader(TDataReader &dataReader) : DataReader(&dataReader),  Remains(0), RemainsNum(0) {
    TTBitReader(typename TDataReader::TSource source) : TDataReader(source),  Remains(0), RemainsNum(0) {}
//    template <typename TSource >
//    TTBitReader(TSource source) : TDataReader(source),  Remains(0), RemainsNum(0) {

/*
    void Read(ui32& result, const ui32 bits) {

        ui64 r64 = 0;
        Read(r64, bits);
        result = (ui32) r64;
    }
*/
    void Read64(ui64& result, const ui32 bits) {
        DEBUG_ASSERT(bits <= 64);
        // next condition is critical for '>> antibits' operations
        // becouse >> 64 do nothing in gcc
        if (bits == 0) {
            result =  0;
            return;
        }

        result = Remains;
        const int bits_needed_from_source = bits - RemainsNum;

        if (bits_needed_from_source < 0) {
			const ui32 antibits = 64 - bits;
			result &= 0xffffffffffffffff >> antibits;
            Remains >>= bits;
            RemainsNum -= bits;
        } else if (bits_needed_from_source > 0) {
            const ui64 bytes_readed = TDataReader::ReadInto(Remains);
//			const ui64 bytes_readed = DataReader::ReadData((char*)&Remains,sizeof(Remains));
			const int bits_readed = bytes_readed<<3;
            if (bits_readed < bits_needed_from_source)
                throw TException(0, "TTBitReader.Read(ui64&, const ui32)");
            if (bits_needed_from_source != 64) {
                result |= (Remains << RemainsNum);
        const ui32 antibits = 64 - bits;
				result &= 0xffffffffffffffff >> antibits;
                Remains >>= bits_needed_from_source;
            } else {
                result = Remains;
                Remains = 0;
            }
            RemainsNum = bits_readed - bits_needed_from_source;
        } else {
            Remains = 0;
            RemainsNum = 0;
        }
    }
	void ReadABit(ui64& result) {
		if (RemainsNum == 0) {
            const ui32 bytes_readed = TDataReader::ReadInto(Remains);
//			const ui32 bytes_readed = DataReader::ReadData((char*)&Remains,sizeof(Remains));
			if (bytes_readed == 0)
				throw TException(0, "void ReadABit(ui64& result)");
            RemainsNum += bytes_readed<<3;
        }
        result = Remains & 1;
        Remains >>= 1;
        RemainsNum--;

/*
//        asm (
//            "mov $0, %0;\
//             sar $1, %1;\
//             adc $0, %0;\
//             dec %2;"
//            :"=r"(result), "=r"(Remains), "=r"(RemainsNum)
//            :"1"(Remains), "2"(RemainsNum)
//            :
//        );
*/
    }
};

template<int direction, class TDataWriter>

class TTBitWriter : public TDataWriter, public IBitWriter
{
private:
    ui32 FreeBits;
    ui64 Active;
    ui64 Offset;

public:
    TTBitWriter(typename TDataWriter::TStorage storage) : TDataWriter(storage), FreeBits(64), Active(0), Offset(0) {
    }

    virtual ~TTBitWriter() {
        Flush();
    }

    ui64 GetOffset() const {
        return Offset;
    }

    void Write(ui64 data, ui32 bits) {
#ifdef DEBUG
//        cout <<"Write"<<direction << " out: " << hex << data << dec << " bits " << bits << endl;
#endif
        if (FreeBits < bits) {
            if (FreeBits != 0) {
                bits -= FreeBits;
                Active |= (data & ((ui64(1) << FreeBits) - 1))
                        << (direction ? (64 - FreeBits) : 0);
                data >>= FreeBits;
                FreeBits = 0;

/*
//                asm(
//                    "sub %0, %2;\
//                     mov %3, %%rax;\
//                     mov $64, %%ecx;\
//                     sub %0, %%ecx;\
//                     sal %%cl, %%rax;\
//                     or %%rax, %1;\
//                     mov %0, %%ecx;\
//                     sar %%cl, %3;\
//                     mov $0, %0;\
//                     "
//                    :"=r"(FreeBits), "=r"(Active), "=r"(bits),"=r"(data)
//                    :"0"(FreeBits),"1"(Active), "2"(bits),"3"(data)
//                    :"%rax","%ecx"
//                );
*/
            }
            Flush();
        }
        Active |= data << (direction ? (64 - FreeBits) : (FreeBits - bits));
        FreeBits -= bits;
        // if (!FreeBits)
        //    Flush();
    }

    template<int bits>
    void WriteWords(ui64 data) {
        const ui64 mask = ((ui64(1) << bits) - 1);
        const ui64 haveMore = ui64(1) << bits;
        do {
            ui64 part = data & mask;
            data >>= bits;
            if (data)
                part |= haveMore;
            Write(part, bits + 1);
        } while (data);
    }

    template<int Bits>
    void WriteWords(ui32& data) {
        ui64 d64 = data;
        WriteWords<Bits>(d64);
    }

    void Flush() {
        if (FreeBits < 64) {
            if (FreeBits) {
                if (direction) {
                    Active &= (ui64(1) << (64 - FreeBits)) - 1;
                    TDataWriter::WriteData((const char*) &Active,
                             sizeof(Active) - (FreeBits >> 3));
                } else {
                    Active &= ~((ui64(1) << FreeBits) - 1);
                    TDataWriter::WriteData((const char*) &Active + (FreeBits >> 3),
                            sizeof(Active) - (FreeBits >> 3));
                }
                Offset += 8 - (FreeBits >> 3);
            } else {
                TDataWriter::WriteData((const char*) &Active, sizeof(Active));
                Offset += 8;
            }
#ifdef DUMP
            cout << "flush: " << hex << Active << dec << endl;
#endif
            FreeBits = 64;
            Active = 0;
        }
    }
};

/*
//template<int direction=1>
//class TBitInput {
//// : public IBitReader
//    const char* Current;
//    const char*& Start;
//    const char*& End;
//    ui32 BitsOffset;

//public:
//    TBitInput(const char** start, const char** end)
//        : Current(*start)
//        , Start(*start)
//        , End(*end)
//        , BitsOffset(0)
//    {
//    }

//    void ReadABit(ui64& result) {
//        Read(result,ui32(1));
//    }

//    bool Read(ui32& result, ui32 bits) {
//        ui64 r64 = 0;
//        bool retCode = Read(r64, bits);
//        result = (ui32)r64;
//        return retCode;
//    }

//    bool Read(ui64& result, ui32 bits) {
////        assert(!Eof()); // todo make it never go there
////        assert(bits < 56);
//        if (direction) {
//            // read from memory interval
//            const ui32 left = End - Current;
//            const ui32 need = bits + BitsOffset;
//            if (left < sizeof(ui64)) {
//                if (need > left << 3)
//                    return false;
//                memcpy((char*)&result, Current, left);
//            } else {
//                result = *(const ui64*)Current;
//            }
//            result >>= BitsOffset;
//            result &= ((ui64(1) << bits) - 1);

//            BitsOffset = need & 7;
//            Current += need >> 3; // bytes shift
//            Start = Current + (BitsOffset != 0);
//        } else {
//            const ui32 left = Current - End;
//            const ui32 need = BitsOffset + bits;
//            if (left < sizeof(ui64)) {
//                if (need > left << 3)
//                    return false;
//                const ui32 bytesToRead = (need >> 3) + ((need & 7) > 0);
//                memcpy((char*)(&result + 1) - bytesToRead, Current - bytesToRead, bytesToRead);
//            } else {
//                result = *((const ui64*)Current - 1);
//            }
//            result >>= 64 - need;
//            result &= ((ui64(1) << bits) - 1);

//            BitsOffset = need & 7;
//            Current -= need >> 3; // bytes shift
//            Start = Current - (BitsOffset != 0);
//        }

////      DEBUG_CODE (      cout << direction << " in: " << hex << result << dec << " bits " << bits << endl; )
//        return true;
//    }
//};
*/

template<int direction = 1>
class TBitInput {
    const char* Current;
    const char*& Start;
    const char*& End;
    ui32 BitsOffset;

public:
    TBitInput(const char** start, const char** end) :
            Current(*start), Start(*start), End(*end), BitsOffset(0) {
    }

    bool Read(ui32& result, ui32 bits) {
        ui64 r64 = 0;
        bool retCode = Read(r64, bits);
        result = (ui32) r64;
        return retCode;
    }

    bool Read(ui64& result, ui32 bits) {
        //assert(!Eof());
        // todo make it never go there
        //assert(bits < 56);
        if (direction) {
            // read from memory interval
            const ui32 left = End - Current;
            const ui32 need = bits + BitsOffset;
            if (left < sizeof(ui64)) {
                if (need > left << 3)
                    return false;
                memcpy((char*) &result, Current,
                        left/*(need >> 3) + ((need & 7) > 0)*/);
            } else {
                result = *(const ui64*) Current;
            }
            result >>= BitsOffset;
            result &= ((ui64(1) << bits) - 1);

            BitsOffset = need & 7;
            Current += need >> 3; // bytes shift
            Start = Current + (BitsOffset != 0);
        } else {
            const ui32 left = Current - End;
            const ui32 need = BitsOffset + bits;
            if (left < sizeof(ui64)) {
                if (need > left << 3)
                    return false;
                const ui32 bytesToRead = (need >> 3) + ((need & 7) > 0);
                memcpy((char*) (&result + 1) - bytesToRead,
                        Current - bytesToRead, bytesToRead);
            } else {
                result = *((const ui64*) Current - 1);
            }
            result >>= 64 - need;
            result &= ((ui64(1) << bits) - 1);

            BitsOffset = need & 7;
            Current -= need >> 3; // bytes shift
            Start = Current - (BitsOffset != 0);
        }
#ifdef DEBUG
        cout << direction << " in: " << hex << result << dec << " bits " << bits
                << endl;
#endif
        return true;
    }

    template<int bits>
    bool ReadWords(ui64& data) {
        data = 0;
        const ui64 haveMore = ui64(1) << bits;
        ui64 current = 0;
        int byteNo = 0;
        while (true) {
            if (!Read(current, bits + 1))
                return false;
            if (!(current & haveMore)) {
                data |= current << (byteNo * bits);
                break;
            } else {
                data |= (current ^ haveMore) << (byteNo++ * bits);
            }
        }
        return true;
    }

    bool Eof() const {
        if (direction)
            return Current >= End;
        else
            return End >= Current;
    }

    const char* GetCurrent() const {
        return Current;
    }
};

template<typename T>
class TBitStreamSerializer {
public:
    template<class TBitOutput>
    void WriteTo(TBitOutput& bout, const T& x) const {
        const ui8* ptr = (const ui8*) &x;
        const ui8* end = ptr + sizeof(T);
        while (ptr < end)
            bout.Write(*ptr++, 8);
    }

    template<class TBitInput>
    bool ReadFrom(TBitInput& bin, T* x) const {
        char* ptr = (char*) x;
        const char* end = ptr + sizeof(T);
        while (ptr < end) {
            ui64 code = 0;
            if (!bin.Read(code, 8))
                return false;
            *ptr++ = code & 0xFF;
        }
        return true;
    }
};
